#include "mixp_config.h"
#include "mixp_config_util.h"
#include "json/cJSON.h"

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

static char *read_file(const char *filename)
{
    FILE *file = NULL;
    long length = 0;
    char *content = NULL;
    size_t read_chars = 0;

    file = fopen(filename, "rb");
    if (file == NULL)
    {
        goto cleanup;
    }

    if (fseek(file, 0, SEEK_END) != 0)
    {
        goto cleanup;
    }
    length = ftell(file);
    if (length < 0)
    {
        goto cleanup;
    }
    if (fseek(file, 0, SEEK_SET) != 0)
    {
        goto cleanup;
    }

    content = (char *)malloc((size_t)length + sizeof(""));
    if (content == NULL)
    {
        goto cleanup;
    }

    read_chars = fread(content, sizeof(char), (size_t)length, file);
    if ((long)read_chars != length)
    {
        free(content);
        content = NULL;
        goto cleanup;
    }
    content[read_chars] = '\0';

cleanup:
    if (file != NULL)
    {
        fclose(file);
    }
    return content;
}

static int mixp_json_conf_load(struct mixp_config *c, const char *name)
{
    cJSON *json;
    char *buf = read_file(name);
    if (!buf)
    {
        printf("read_file %s failed!\n", name);
        return -1;
    }
    json = cJSON_Parse(buf);
    if (!json)
    {
        printf("cJSON_Parse failed!\n");
        free(buf);
        return -1;
    }
    free(buf);
    c->priv = (void *)json;
    strncpy(c->path, name, sizeof(c->path));
    return 0;
}

static char *mixp_json_get_string(struct mixp_config *c, ...)
{
    cJSON *json = (cJSON *)c->priv;

    struct int_charp *type_list = NULL;
    struct int_charp mix;
    int cnt = 0;
    int i;
    va_list ap;

    va_start(ap, c);
    va_arg_type(ap, mix);
    while (mix.type != TYPE_EMPTY)
    { //last argument must be NULL
        cnt++;
        type_list = (struct int_charp *)realloc(type_list, cnt * sizeof(struct int_charp));
        memcpy(&type_list[cnt - 1], &mix, sizeof(mix));
        va_arg_type(ap, mix);
    }
    va_end(ap);

    for (i = 0; i < cnt; i++)
    {
        switch (type_list[i].type)
        {
        case TYPE_INT:
            json = cJSON_GetArrayItem(json, type_list[i].ival - 1);
            break;
        case TYPE_CHARP:
            json = cJSON_GetObjectItem(json, type_list[i].cval);
            break;
        default:
            break;
        }
    }
    free(type_list);

    if (cJSON_IsString(json))
    {
        return json->valuestring;
    }
    else
    {
        return "";
    }
}

static int mixp_json_get_int(struct mixp_config *c, ...)
{
    cJSON *json = (cJSON *)c->priv;
    struct int_charp *type_list = NULL;
    struct int_charp mix;
    int cnt = 0;
    int i;
    va_list ap;

    va_start(ap, c);
    va_arg_type(ap, mix);
    while (mix.type != TYPE_EMPTY)
    { //last argument must be NULL
        cnt++;
        type_list = (struct int_charp *)realloc(type_list, cnt * sizeof(struct int_charp));
        memcpy(&type_list[cnt - 1], &mix, sizeof(mix));
        va_arg_type(ap, mix);
    }
    va_end(ap);

    for (i = 0; i < cnt; i++)
    {
        switch (type_list[i].type)
        {
        case TYPE_INT:
            json = cJSON_GetArrayItem(json, type_list[i].ival - 1);
            break;
        case TYPE_CHARP:
            json = cJSON_GetObjectItem(json, type_list[i].cval);
            break;
        default:
            break;
        }
    }
    free(type_list);

    if (cJSON_IsNumber(json))
    {
        return json->valueint;
    }
    else if (cJSON_IsString(json))
    {
        return atoi(json->valuestring);
    }
    else
    {
        return 0;
    }
}

static double mixp_json_get_double(struct mixp_config *c, ...)
{
    cJSON *json = (cJSON *)c->priv;
    struct int_charp *type_list = NULL;
    struct int_charp mix;
    int cnt = 0;
    int i;
    va_list ap;

    va_start(ap, c);
    va_arg_type(ap, mix);
    while (mix.type != TYPE_EMPTY)
    { //last argument must be NULL
        cnt++;
        type_list = (struct int_charp *)realloc(type_list, cnt * sizeof(struct int_charp));
        memcpy(&type_list[cnt - 1], &mix, sizeof(mix));
        va_arg_type(ap, mix);
    }
    va_end(ap);

    for (i = 0; i < cnt; i++)
    {
        switch (type_list[i].type)
        {
        case TYPE_INT:
            json = cJSON_GetArrayItem(json, type_list[i].ival - 1);
            break;
        case TYPE_CHARP:
            json = cJSON_GetObjectItem(json, type_list[i].cval);
            break;
        default:
            break;
        }
    }
    free(type_list);

    if (cJSON_IsNumber(json))
    {
        return json->valuedouble;
    }
    else if (cJSON_IsString(json))
    {
        return atof(json->valuestring);
    }
    else
    {
        return 0;
    }
}

static int mixp_json_get_boolean(struct mixp_config *c, ...)
{
    cJSON *json = (cJSON *)c->priv;
    struct int_charp *type_list = NULL;
    struct int_charp mix;
    int cnt = 0;
    int i;
    va_list ap;

    va_start(ap, c);
    va_arg_type(ap, mix);
    while (mix.type != TYPE_EMPTY)
    { //last argument must be NULL
        cnt++;
        type_list = (struct int_charp *)realloc(type_list, cnt * sizeof(struct int_charp));
        memcpy(&type_list[cnt - 1], &mix, sizeof(mix));
        va_arg_type(ap, mix);
    }
    va_end(ap);

    for (i = 0; i < cnt; i++)
    {
        switch (type_list[i].type)
        {
        case TYPE_INT:
            json = cJSON_GetArrayItem(json, type_list[i].ival - 1);
            break;
        case TYPE_CHARP:
            json = cJSON_GetObjectItem(json, type_list[i].cval);
            break;
        default:
            break;
        }
    }
    free(type_list);

    if (cJSON_IsTrue(json) || (cJSON_IsString(json) && !strcasecmp(json->valuestring, "true")))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

static void mixp_json_conf_unload(struct mixp_config *c)
{
    cJSON *json = (cJSON *)c->priv;
    if (json)
    {
        cJSON_Delete(json);
        json = NULL;
    }
}

struct mixp_config_ops mixp_json_ops = {
    mixp_json_conf_load,
    mixp_json_get_string,
    mixp_json_get_int,
    mixp_json_get_double,
    mixp_json_get_boolean,
    NULL,
    mixp_json_conf_unload,
};